package org.eclipse.iota.rules.basic;

import org.eclipse.emf.ecore.EObject;
import org.eclipse.iota.arithmetics.IotaOperationExecutor;
import org.eclipse.iota.model.Addition;
import org.eclipse.iota.model.Division;
import org.eclipse.iota.model.Equation;
import org.eclipse.iota.model.Expression;
import org.eclipse.iota.model.Multiplication;
import org.eclipse.iota.rewrite.AbstractIotaRule;
import org.eclipse.iota.util.IotaCopyUtil;
import org.eclipse.iota.util.IotaFindUtil;
import org.eclipse.iota.util.IotaReplaceUtil;

/**
 * Rule for eliminating divisions.
 * @author Christian Krause
 * @generated NOT
 */
public class EliminateDivision extends AbstractIotaRule {
	
	/*
	 * (non-Javadoc)
	 * @see org.eclipse.iota.rewrite.AbstractIotaRule#apply(org.eclipse.iota.model.Division, boolean)
	 */
	@Override
	public boolean apply(Division division, boolean change) {
		
		// Check preconditions.
		Addition addition = findAddition(division);		
		if (findEquation(division)==null && addition==null) {
			return false;
		}
		
		// Equation and divisor.
		Equation equation = division.getEquation();
		Expression divisor = IotaCopyUtil.copy(division.getRight());
		
		// Perform the action.
		if (change) {
		
			// Check which side the division belongs to.
			if (contains(equation.getLeft(),division)) {
				IotaOperationExecutor.multiply(equation.getRight(), divisor);
			} else {
				IotaOperationExecutor.multiply(equation.getLeft(), divisor);
			}

			if (addition!=null) {
				for (Expression summand : IotaCopyUtil.copyOperands(addition)) {
					if (!contains(summand,division)) {
						divisor = IotaCopyUtil.copy(divisor);
						IotaOperationExecutor.multiply(summand, divisor);
					}
				}
			}

			// Replace the division object by its left hand side.
			IotaReplaceUtil.replace(division, division.getLeft());

		}
		
		// Done.
		return true;
		
	}
	
	/* ----- Helper methods ------ */
	
	private boolean contains(Expression container, Expression expression) {
		return IotaFindUtil.getAllExpressions(container, expression.eClass()).contains(expression);
	}
	
	private Equation findEquation(Expression expression) {
		EObject parent = expression.eContainer();
		if (parent instanceof Equation) return (Equation) parent;
		if (parent instanceof Multiplication) return findEquation((Expression) parent);
		if (parent instanceof Division) return findEquation((Expression) parent);
		return null;
	}
	
	private Addition findAddition(Expression expression) {
		EObject parent = expression.eContainer();
		if (parent instanceof Addition) {
			// Make sure all other parents are compatible.
			if (findEquation((Expression) parent)!=null) return (Addition) parent;
		}
		if (parent instanceof Multiplication) return findAddition((Expression) parent);
		if (parent instanceof Division) return findAddition((Expression) parent);
		return null;
	}
		
}
